#pragma once
#include "../cppx.luavm/RemoteEngine.h"
#include "DebugEvent.h"
#include "Mediator.h"
#include <boost/function.hpp>
#include <wx/treelistctrl/treelistctrl.h>
#include <wx/settings.h>


LUA_DEBUG_BEGIN

/// The type of a function that requests LuaVarList from RemoteEngine.
typedef boost::function<void(const LuaVarListCallback &)> VarListRequester;

typedef std::vector<wxTreeItemId> wxTreeItemIdList;

/**
* @brief The data of the VariableWatch.
*/
class VariableWatchItemData : public wxTreeItemData
{
public:
    explicit VariableWatchItemData(Mediator_ptr mediator,const LuaVar &var)
        : m_mediator(mediator), m_var(var), m_requestCount(-1), m_updateCount(-1) {
    }

    virtual ~VariableWatchItemData() {
    }

    /// Get the LuaVar object.
    const LuaVar &GetVar() const {
        return m_var;
    }

    /// Get the request count.
    int GetRequestCount() const {
        return m_requestCount;
    }

    /// Requested this var.
    void Requested() {
        int count = m_mediator->GetUpdateCount();

        if (m_requestCount < count) {
        	m_requestCount = count;
        }
    }

    /// Get the update count.
    int GetUpdateCount() const {
        return m_updateCount;
    }

    /// Update the update count.
    void Updated() {
        int count = m_mediator->GetUpdateCount();

        if (m_requestCount < count) {
        	m_requestCount = count;
        }
        if (m_updateCount < count) {
        	m_updateCount = count;
        }
    }

private:
    Mediator_ptr m_mediator;
    LuaVar m_var;
    int m_requestCount;
    int m_updateCount;
};

/**
* @brief The common implementation of 'VariableWatch'.
*/
class VariableWatch : public wxTreeListCtrl
{
    Mediator_ptr m_mediator;

public:
    VariableWatch(Mediator_ptr mediator,
        wxWindow *parent, int id,
        bool isShowColumn, 
        bool isShowType,
        bool isLabelEditable, 
        bool isEvalLabels,
        const VarListRequester &requester);

    virtual ~VariableWatch();

    /// Get children of the item.
    virtual wxTreeItemIdList GetItemChildren(const wxTreeItemId &item);

    /// Get the data of the item.
    virtual VariableWatchItemData *GetItemData(const wxTreeItemId &item);

private:
    /// This object is called when the request var list
    /// is done and results are returned.
    struct RequestVarListCallback
    {
        explicit RequestVarListCallback(Mediator_ptr mediator,VariableWatch *watch, wxTreeItemId item, bool isExpanded)
            : m_mediator(mediator),m_watch(watch), m_item(item)
            , m_isExpanded(isExpanded), m_updateCount(mediator->GetUpdateCount())
        {
        }

        /// This method may be called from the other thread.
        /// @param vars    result of the request
        int operator()(const LuaCommand &/*command*/, const LuaVarList &vars) {
            // If update count was changed, new request might be sent.
            if (m_updateCount != m_mediator->GetUpdateCount()) {
            	return -1;
            }

            // Is m_watch still alive ?
            if (ms_aliveInstanceSet.find(m_watch) == ms_aliveInstanceSet.end()) {
                return -1;
            }

            m_watch->DoUpdateVars(m_item, vars, m_isExpanded);
            return 0;
        }

    private:
        Mediator_ptr m_mediator;
        VariableWatch *m_watch;
        wxTreeItemId m_item;
        bool m_isExpanded;
        int m_updateCount;
    };

    friend struct RequestVarsCallback;

public:
    /// Begin updating contents.
    void BeginUpdating(wxTreeItemId item, bool isExpanded,const VarListRequester &request);

    /// Request for the fields of the var.
    struct FieldsRequester
    {
        explicit FieldsRequester(Mediator_ptr mediator,const LuaVar &var)
            : m_mediator(mediator),m_var(var)
        {
        }
        void operator()(const LuaVarListCallback &callback) {
            m_mediator->GetEngine()->SendRequestFieldsVarList(m_var, callback);
        }
    private:
        Mediator_ptr m_mediator;
        LuaVar m_var;
    };

    /// Begin the updating the fields of the var.
    void BeginUpdating(wxTreeItemId item, bool isExpanded, const LuaVar &var);

    /// Request for the results of the label evaluations.
    struct EvalLabelRequester {
        explicit EvalLabelRequester(Mediator_ptr mediator,VariableWatch *watch)
            : m_mediator(mediator),m_watch(watch) {
        }
        void operator()(const LuaVarListCallback &callback) {
            wxTreeItemId item = m_watch->GetRootItem();
            wxTreeItemIdValue cookie;
            string_array labels;

            for (wxTreeItemId child = m_watch->GetFirstChild(item, cookie);
                child.IsOk();child = m_watch->GetNextChild(item, cookie))
            {
                wxString label = m_watch->GetItemText(child);

                if (label.Strip(wxString::both).IsEmpty()) {
                    labels.push_back("");
                }
                else {
                    std::string str = "return ";
                    //str += wxConvToCtxEnc(label);
                    str += label;
                    labels.push_back(str);
                }
            }

            m_mediator->GetEngine()->SendEvalsToVarList(labels,m_mediator->GetStackFrame(),callback);
        }
    private:
        Mediator_ptr m_mediator;
        VariableWatch *m_watch;
    };

    /// Begin updating vars.
    void BeginUpdating();

    /// Clear this view.
    void Clear();

private:
    /// Compare the item vars.
    struct ItemComparator {
        explicit ItemComparator(VariableWatch *watch, const LuaVar &var)
            : m_watch(watch), m_var(var) {
        }

        bool operator()(const wxTreeItemId &item) {
            VariableWatchItemData *data = m_watch->GetItemData(item);
            return
                ( data != NULL && data->GetVar().IsOk()
                ? data->GetVar().GetName() == m_var.GetName()
                : false);
        }

    private:
        VariableWatch *m_watch;
        const LuaVar &m_var;
    };

    /// Update child variables of vars actually.
    void DoUpdateVars(wxTreeItemId parent, const LuaVarList &vars, bool isExpand);

private:
    void OnExpanded(wxTreeEvent &event);
    void OnEndLabelEdit(wxTreeEvent &event);

private:
    /// Get the width that can show vars.
    int GetContentWidth();

    /// Resize the target and next columns to fit the window width.
    void LayoutColumn(int targetColumn);

    /// Fit the the columns to the window width.
    void FitColumns();

    void OnSize(wxSizeEvent &event);
    void OnColEndDrag(wxListEvent &event);
    void OnEndDebug(wxDebugEvent &event);

private:
    typedef std::set<VariableWatch *> InstanceSet;
    static InstanceSet ms_aliveInstanceSet;

    bool m_isLabelEditable;
    bool m_isEvalLabels;
    VarListRequester m_requester;

    DECLARE_EVENT_TABLE();
};

LUA_DEBUG_END
